vim9script noclear

highlight default JumpFocus			guifg=#958c6a
highlight default JumpMain			guifg=#f2119c gui=bold cterm=bold

const limit = 200
var table: list<dict<any>>
var ids: list<number>

def Init()
	var id: string
	var str: list<string> = 'fjdklshgaewiomc'->split('\zs')
	var len: number = str->len()

	ids = []
	table = []
	for idx in range(limit)
		table->add({'name': str[idx / len] .. str[idx % len]})

		id = table[idx]['name']
		ids[idx] = popup_create(id, {hidden: true, wrap: false, tabpage: -1, highlight: "JumpMain"})
		table[idx][id] = ids[idx]
	endfor
enddef

# https://github.com/girishji/vimbits/blob/8d17dfe7d21435fddf5ae165341dc6f971ab2a25/autoload/vimbits/jump.vim#L143
def ScreenPos(lnum: number, col: number): list<number>
    var is_invisible = {row: 0, col: 0, endcol: 0, curscol: 0}
    var scrpos = win_getid()->screenpos(lnum, col)
    if scrpos != is_invisible
        # screenpos has no knowledge of concealed or substituted chars
        var concealed_len = 0
        var idx = 1
        while idx < col
            var status = synconcealed(lnum, idx)
            if status[0] == 1  # concealed
                var spos = win_getid()->screenpos(lnum, idx)
                var clen = spos.endcol - spos.col + 1
                if status[1] != null_string  # substitute char present
                    echom status[1]
                    # Vim does not allow/show <tab> as substitute char
                    # utf-8 chars are either 1 cell or 2 cell wide
                    clen -= status[1]->strwidth()  # display width in cells
                endif
                concealed_len += clen
                var line = getline(lnum)
                idx += line->strcharpart(line->charidx(idx - 1), 1)->len()  # skip by byte width of char
            else
                idx += 1
            endif
        endwhile
        return [scrpos.row, scrpos.col - concealed_len]
    endif
    return [-1, -1]
enddef

def Show(lnum: number, end: number)
	var [len, id, ch, input, screen_pos] = [0, 0, '', '', {}]
	var nr: number = winnr()

	id = matchaddpos("JumpFocus", range(lnum, end))
	defer matchdelete(id)
	redraw

	ch = getchar(-1, {cursor: "hide", number: v:false})
	len = foreach(matchbufline(bufnr(), '\<\k\+\>', lnum, end)
		->filter((_, v) => v.text =~? ch), (k, v) => {
				table[k]['pos'] = v
			})->len()

	if len == 0
		return
	elseif len >= limit
		len = limit
	endif

	for idx in range(len)
		if &ft == 'help'
			var [row, col] = ScreenPos(table[idx].pos.lnum, table[idx].pos.byteidx + 1)
			popup_move(table[idx][table[idx].name], {line: row, col: col})
		else
			screen_pos = screenpos(nr, table[idx].pos.lnum, table[idx].pos.byteidx + 1)
			popup_move(table[idx][table[idx].name], {line: screen_pos.row, col: screen_pos.curscol})
		endif

		popup_show(table[idx][table[idx].name])
	endfor
	redraw

	for _ in range(2)
		input ..= getchar(-1, {cursor: "hide", number: v:false})

		if input !~# '\l'
			break
		endif
	endfor

	for idx in range(len)
		if table[idx]->has_key(input)
			cursor(table[idx].pos.lnum, table[idx].pos.byteidx + 1)
		endif

		popup_hide(table[idx][table[idx].name])
	endfor
enddef

export def Jump()
	if table->empty() || popup_getoptions(ids[0])->empty()
		popup_clear()

		Init()
	endif

	Show(line('w0'), line('w$'))
enddef
